package org.example;

import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.util.concurrent.locks.LockSupport;

/**
 * 先看SimulatedCASDemo和MethodHandlesDemo
 * 自旋锁就是指当有另外一个线程来竞争锁时，这个线程会在原地循环等待，而不是把该线程给阻塞，直到那个获得锁的线程释放锁之后，这个线程就可以马上获得锁的。
 * 注意，锁在原地循环的时候，是会消耗cpu的，就相当于在执行一个啥也没有的for循环。
 * 所以，轻量级锁适用于那些同步代码块执行的很快的场景，这样，线程原地等待很短很短的时间就能够获得锁了。
 * 自旋锁的一些问题
 * <p>
 * 如果同步代码块执行的很慢，需要消耗大量的时间，那么这个时侯，其他线程在原地等待空消耗cpu，这会让人很难受。
 * 本来一个线程把锁释放之后，当前线程是能够获得锁的，但是假如这个时候有好几个线程都在竞争这个锁的话，那么有可能当前线程会获取不到锁，
 * 还得原地等待继续空循环消耗cup，甚至有可能一直获取不到锁。
 * <p>
 * 基于这个问题，我们必须给线程空循环设置一个次数，当线程超过了这个次数，我们就认为，继续使用自旋锁就不适合了，此时锁会再次膨胀，升级为重量级锁。
 * 默认情况下，自旋的次数为10次，用户可以通过-XX:PreBlockSpin来进行更改。
 * <p>
 * public class AtomicInteger extends Number implements java.io.Serializable {
 * <p>
 * private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
 * private static final long VALUE = U.objectFieldOffset(AtomicInteger.class, "value");
 * <p>
 * private volatile int value;
 * public final boolean compareAndSet(int expectedValue, int newValue) {
 * return U.compareAndSetInt(this, VALUE, expectedValue, newValue);
 * }
 * }
 */
public class CASLockDemo {
    private volatile int lock = 1;
    private static final VarHandle casLock;

    static {
        try {
            //创建一个Lookup对象，该对象可以创建所有访问权限的方法，包括public，protected，private，default。
            MethodHandles.Lookup l = MethodHandles.lookup();
            casLock = l.findVarHandle(CASLockDemo.class, "lock", int.class);
        } catch (ReflectiveOperationException e) {
            throw new ExceptionInInitializerError(e);
        }

        // Reduce the risk of rare disastrous classloading in first call to
        // LockSupport.park: https://bugs.openjdk.java.net/browse/JDK-8074773
        Class<?> ensureLoaded = LockSupport.class;
    }


    public int getSharedState() {
        return lock;
    }

    public void doYourWork() throws InterruptedException {
        // 第一次不让通过,模拟别的线程先占用了。
        int expactValue = 0;
        int newValue = expactValue + 1;

        //模拟使用自旋锁进入临界区(同步代码区)；
        while (!casLock.compareAndSet(this, expactValue, newValue)) {
            mockingWaiting(3);
            //mockingWaiting(6); //模拟自旋达到上限

            // 模拟其他线程修改完成释放锁
            otherThreadReleaseLock();
        }
        System.out.println("I get the lock : " + getSharedState());//输出1

    }

    private void otherThreadReleaseLock() {
        System.out.println("B finished, release lock");
        casLock.compareAndSet(this, 1, 0);
    }

    private static void mockingWaiting(int waitingTime) throws InterruptedException {
        for (int i = 0; i < waitingTime; i++) {
            System.out.println("waiting for lock");
            Thread.sleep(1000);
        }

        if (waitingTime > 5) {
            throw new IllegalArgumentException();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        CASLockDemo casLockDemo = new CASLockDemo();
        casLockDemo.doYourWork();
    }
}
